J2SE复习

1. String两种实例化方式的区别

(1)直接赋值方式,创建的对象存放到字符串对象池(constant pool,常量池)里,假如已经存在,就不会再创建;
(2)new对象方式,每次都创建一个新的对象。
一般直接输出实例对象,与toString()方法作用相同。

2. equals()与==的区别

默认情况下它们的作用相等,在比较基本类型的时候,比较它们的实际值是否相等;在比较引用类型的时候,比较它们引用的地址是否相等(即是否引用的同一对象)。
所以通常需要重写(Override)equals()方法,使得它比较的是实际值是否相等(即我们通常认为的值相等)。

3. String类常用方法

char charAt(int index):返回指定索引的char值
int indexOf(char cha):返回cha字符第一次出现的索引
int length()
String substring(int beginIndex, int endIndex)
String toUpperCase():全部转换成大写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/***统计字符串中各种类型的个数***/
for(int i = 0; i < str.length(); i++){
char c = str.charAt(i);
if ( …){

b++;
}else if(…){

c++;
}
}
/***字符串翻转***/
String str = “abc”;
String newStr = “”;
for(int i = str.length() – 1; i >= 0; i--){
char c = str.charAt(i);
newStr+= c;
}
System.out.println(newStr);
/***将一串数字字符转换成数组***/
String str = “1, 3, 5, 7”;
int digitalLen = 0;
for (int i = 0; i < str.length, i++){
if ( str.charAt(i) != “,”){
digitalLen++;
}
}
int[] arr = new int[digitalLen];
int j = 0;
for (int i = 0; i < srt.length(); i++){
if (str.charAt(i) != “,”){
arr[j] = Interger.parseInt(str.charAt(i) + “”); //将字符串转换成int类型
j++;
}
}
for (int a : arr){
System.out.println(a + “”);
}

4. 私有(private)方法不能继承

私有的变量如果有public的getter和setter方法,它也是可以在子类中被修改的。

5. final

使用final声明的类不能被继承;
使用final声明的方法不能被子类覆盖;
使用final声明的变量不能被修改,即为常量。通常是final static一起用来定义不可变的静态常量,使用类名.变量进行调用。

6. 抽象类

在java中,含有抽象方法的类称为抽象类,同样不能生成对象。
继承(extends)抽象类
注意点:
(1) 包含一个抽象方法的类是抽象类;(与接口的区别:没有普通方法,由全局常量和公共的抽象方法所组成)
(2) 抽象类和抽象方法都要用abstract关键字修饰;
(3) 抽象方法只需要声明而不需要实现;
(4) 抽象类必须被子类(假如不是抽象类)重写抽象中的全部抽象方法;
(5) 抽象类不能被实例化。

7. 接口

接口中没有普通方法,由全局常量和公共的抽象方法所组成。
实现(implements)接口
public static final String str = “abc”;
public (abstract) void fangfa(){}
注意点:
(1) 由于接口里的方法都是抽象的,所以abstract可以省略,实际开发一般都是省略的,开发者的习惯。
(2) 实现多个接口,中间用逗号隔开,并且实现所有抽象方法,如public class Test implements A, B{ }
(3) 若既要继承C类,又要实现A, B接口:先继承,后实现接口。
(4) 接口可以多继承,如public interface A extends B{ }

8. 多态性

(1) 方法的重载和重写。
(2) 可以用父类的引用指向子类的具体实现,而且可以随时更换为其他子类的具体实现。假设父类Animal类,子类Dog类和Cat类都实现了父类方法。

1
2
3
4
5
6
//父类引用指向子类的具体实现
Animal animal = new Dog(); // 对象向上转型,安全
animal.say(); // 我是一只狗
//更换实现
animal = new Cat();
animal.say(); // 我是一只猫

(3) 对象的转型:
向上转型:子类对象->父类对象 安全
向下转型:父类对象->子类对象 不安全,需要强制类型转换。
如:

1
2
3
Animal animal = new Dog(); // 对象向上转型,安全
animal.say(); // 我是一只狗
Dog dog = (Dog) animal; // 向下转型,不安全,强制类型

9. Object类

Object类的常用方法:
(1)public String toString():返回该对象的字符串表示。引用p与p.toString()输出的相同:默认输出的是 包名@标识数字。一般需要重写(override)toString()方法。
(2)public boolean equals(Object obj):两对象值是否相等。
默认equals 是比较对象的引用,是否指向同一个堆内存。

10. instanceof关键字

作用:判断一个对象是否属于一个类。由于一般向下不安全,需要强制类型转换,instanceof可以确保向下转型不出现问题。
格式:boolean 对象 instanceof 类

1
2
3
4
5
if(animal instanceof Dog){
((Dog) animal).say();
}else if(animal instanceof Cat){
((Cat) animal).say();
}

11. 匿名内部类

匿名内部类是没有名字的内部类,它只使用一次。它是一个接口,没有实现(即没有实现类)。

1
2
3
4
5
6
7
8
9
10
public interface A{
public void a();
}
//A接口有具体实现,B类实现A接口
public class B implements A{
@Override
public void a(){
System.out.println(“a方法”);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//A接口没有具体实现
public class Test{
public void test(A a){ //A是一个接口
a.a();
}
public static void main(String[] args){
Test t = new Test();
t.test(new B());
//匿名内部类
t.test(new A(){
@Override
public void a(){
System.out.println(“匿名内部类,一次性使用”);
}
});
}
}

11. 包装类

(1)每个基本类型都有对应的包装类。出现包装类的原因是,java几乎所有要处理的东西都是对象,操作对象比操作基本类型更方便,而操作基本类型更高效。基本类型存储的都是数据量比较小的数据。
注意:特殊的是int-Integer,char-Character,其它都是大写首字母。
int a = 1;
Integer b = new Integer(a); //装箱
int c = b.intValue(); //拆箱
(2)java5.0之前需要显示转换基本类型和包装类。java5.0之后出现了自动装箱(autoboxing)和自动拆箱(unboxing),不必显示转换。
Integer a = 10; // 自动装箱,将int型转换成Integer类型
int b = a; // 自动拆箱,将Integer类型转换成int类型
(3)valueOf()把String类型转换成int类型
String str = “a”;
int a = Integer.valueOf(str);

12. 单例模式(Singleton)

在长期的程序设计过程中,开发者们总结出了很多设计经验,最终经过整理和优化,变成了如今的设计模式。设计模式有很多 有单例、工厂、代理等等。具体参见设计模式
最常用的是单例模式:
在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。单例模式有两种实现,一种是饿汉式,一种是懒汉式。
1)饿汉式
先在内部new,定义实例。在内部定义一个静态常量对象,然后再写一个static方法来返回这个对象,这样保证是一个对象了。

1
2
3
4
5
6
7
8
9
public class Singleton1{
private Singleton1(){} //构造方法私有
//饿汉式,先实例化
private static final Singleton1 single1 = new Singleton1();
//通过方法获取实例
public static Singleton1 getInstance(){
return single1;
}
}

2)懒汉式
先定义引用,当第一次调用getInstance方法时进行实例化。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton2{
private Singleton2(){} //构造方法私有
//懒汉式,先定义引用
private static Singleton2 singleton2;
//通过同步方法获取实例
//考虑到多并发的情况 多个线程同时调用这个方法的时候,会出现问题,所以我们加了同步锁 synchronized 来保证同一时刻只有一个线程进入方法
public synchronized static Singleton2 getInstance(){
if ( singleton2 == null){
singleton2 = new Singleton2();
}
return singleton2;
}
}

由于getInstance()方法都是static方法,因此在main函数中只需类.getInstance()方式来实例化单例类。如

1
2
3
4
5
6
public class Test{
public static void main(String[] args){
Singleton1 singleton1 = Singleton1.getInstance();
Singleton2 singleton2 = Singleton2.getInstance();
}
}

注意:
子类继承父类时并不会继承父类的构造器,需要在子类构造器的第一句写super关键字进行调用。调用子类构造器来初始化对象时,父类构造器总会在子类构造器之前执行
求面积 return (float) (Math.PI * r * r);

13. 异常处理

1) 捕获处理异常用try…catch,最后用finally,finally块一定会被执行,可保证打开的资源被回收。
Java的所有非正常情况分为错误(Error)和异常(Exception),而Exception又可分为Checked异常和Runtime异常(运行时异常)。
注意:处理多个异常,异常由上往下必须范围同级别或者更高,否则编译报错。
2) throws抛出异常:当前方法不处理异常,而是交给方法的调用者去处理。
当前不知道如何处理这种异常->交给上一级(调用者)处理->如果main()也不知道如何处理异常->交给JVM处理(常用的是在main()方法中throws异常),JVM打印跟踪栈信息,并终止程序。
程序的健壮性(robustness):即程序在执行过程中处理错误、异常时继续正常运行的能力。
3) throw:直接抛出一个异常实例,而不是异常类,即throw new ***Exception(***);
实际情况可能程序只对异常进行部分处理,还需要再次抛出异常,交给该方法的调用者处理。throw通常与catch、throws一起使用:在catch中再次抛出(throw)异常,当前方法throws交给上级调用者处理。
4) 自定义异常类:自定义异常类通常需要定义两个构造器:一个无参构造器,一个带字符串参数的构造器。这两个构造器作为异常对象getMessage()方法的返回值。
(1) 自定义异常应该继承Exception基类。Exception是检查型异常,编译时会检查,编译时就出现异常,在程序中必须使用try…catch进行处理;
(2) 如果自定义Runtime异常,则应该继承RuntimeException基类。RuntimeException是非检查型异常,编译时不检查,运行时出现异常,例如NumberFormatException,可以不使用try…catch进行处理(但最好也用try…catch捕获),如果产生异常,则异常将由JVM进行处理。
编译出现异常差不多就是在eclipse中写代码时,代码下面出现红色波浪线。

14. 日期类

java.util.Data类,过时了。
(1)java.util.Calendar类
Calendar类是一个抽象类,因此不能直接new来创建实例,它使用getInstance()方法来创建实例。其构造器用protected秀死,通常使用protected修饰一个方法,是希望子类来重写这个方法。
注意:第一个月从0开始,因此需要get(Calender.MONTH) + 1。
(2)java.text.SimpleDateFormat类:日期类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FormatDate {
/**
* 将日期字符串转换成Date对象
* @param dateStr 日期字符串
* @param format 格式
* @return
* @throws ParseException 格式不正常抛异常
*/
public static Date dateFormat(String dateStr, String format) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.parse(dateStr);
}
/**
* 将Date对象转换为指定格式的字符串
* @param date 传入的日期对象
* @param format 格式
* @return
*/
public static String formatDate(Date date, String format){
String result = "";
SimpleDateFormat sdf = new SimpleDateFormat(format);
if ( date != null){
result = sdf.format(date);
}
return result;
}
public static void main(String[] args) throws ParseException{
Date date = new Date();
System.out.println(formatDate(date,"yyyy-MM-dd"));
//System.out.println(formatDate(date,"yyyy年MM月dd日 HH:mm:ss"));
String dateStr = "1994-01-01 12:00:00";
Date date2 = dateFormat(dateStr, "yyyy-MM-dd HH:mm:ss");
System.out.println(formatDate(date2, "yyyy-MM-dd HH:mm:ss"));
}
}
/*
*今天星期即,英文中从星期日开始
*/
String[] weekDays = {“星期日”, “星期一”, “星期二”, “星期三”, “星期四”, “星期五”, “星期六”,};
Calendar calendar = Calendar.getInstance();
String today = weekDays[calendar.get(Calendar.DAY_OF_WEEK)-1];

15.String和StringBuffer

1)String:对String类型的对象操作,等同于重新生成一个新对象,然后将引用指向它。
String str = “123”;
str += “abc”;
2)StringBuffer:对StringBuffer类型的对象操作,操作的始终是同一个对象。
StringBuffer sb = new StringBuffer(“123”);
sb.append(“abc”);
System.out.println(sb.toString()); //默认sb调用的也是toString()方法
使用:假如定义的字符串内容基本不变或者很少变化,用String效率高;假如定义的字符串内容经常变动,要用StringBuffer。

16. Math类、Arrays类

1) Math类的常用静态方法:max()、min()、round()四舍五入、pow()求次幂等。
Math.max(1,2)
Math.round(3.4)
2) Arrays类主要是封装了很多操作数组的工具方法,方便开发者直接调用.
常用方法:
(1) toString()方法 返回指定数组内容的字符串表示形式。
int[] arr = [1,2,3];
System.out.println(arr); // 输出的是一堆数字@数值
System.out.println(Arrays.toString(arr)); // 将数组转换成String类型输出
(2) sort()方法 对指定的类型数组按数字升序进行排序。
Arrays.sort(arr); //默认升序
System.out.println(Arrays.toString(arr));
(3) binarySearch()方法 使用二分搜索法来搜索指定类型数组,以获取指定值。
System.out.println(Arrays.binarySearch(arr,3); //用二分法搜索arr中3的位置
(4) fill()方法,将指定类型值填充到数组中。
Arrays.fill(arr,0); //将arr初始化
System.out.println(Arrays.toString(arr)); //输出为[0, 0, 0]

17. 泛型(Generic)

1) 泛型可以指代任意对象类型。在集合接口、类后增加尖括号,尖括号里放一个数据类型,即表明这个集合接口、集合类只能保存特定类型的对象。如List = new Array<>();
当创建了带泛型声明的接口,可以为该泛型接口创建实现类;当创建了带泛型声明的父类,可以为该父类创建子类。但是使用这些接口或父类时,就不能再带泛型参数了,必须指定泛型参数类型。
不存在泛型类,所以instanceof运算符后不能使用泛型类。不管泛型的设计类型参数是什么,它们运行时总是有相同的类。如:

1
2
3
List<String> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
l1.getClasss() == l2.getClass(); // true

2) 限制泛型类型:设定类型形参的上限
<T extends Number>
3) 类型通配符?
<? extends A> A是类型通配符?的上限,通配符?必须是A的子类或A本身。
注意:可以设置类型通配符?的上限,也可以设定类型形参的上限
4) 泛型方法(Generic method):返回值和参数都用泛型表示的方法。与普通方法的不同之处,就在于泛型方法名多了类型参数声明。格式:

1
2
3
修饰符 <T, S> 返回值类型 方法名(形参列表){
//方法体
}

18. 反射

一般情况下,我们知道一个类,可以通过这个类创建对象;但是如果要求通过一个对象找到一个类,这时候反射就派上用场了。
java反射实现的核心就是 java.lang.Class类。
Person p = new Student(); // p变量的编译时类型为Person,运行时类型为Student。为了使程序在运行时发现对象和类的真实信息,有两个办法:

  • 假设编译时和运行时都完全知道类型的具体信息,使用instanceof运算符进行判断,再利用类型强制转换将其转换成其运行时类型的变量。
  • 编译时根本无法预知该对象和类可能属于哪些类,只能依靠运行时信息来获取真实信息,需要使用反射。

反射:可以通过反射获取整个类的结构,包括属性(field)、方法(method)、构造方法(constructor)等。三种反射场景:
1) 通过Class类获取对象a的完整包路径名
Student student = new Student();
System.out.println(student.getClass().getName()); //对象.getClass()调用的是Object类的getClass()得到Class对象;然后调用Class里的getName()方法,获取完整包路径类名。
2)通过完整包路径名来实例化Class对象

1
2
3
4
5
6
try{
Class<?> c = Class.forName(“com.Licht._18.Student”);
System.out.println(c.getName());
}Catch (ClassNotFoundException e){
e.printStackTrace();
}

3)通过完整包路径类型来实例化Class对象,通过反射获取Class对象的构造器,通过构造器实例化对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Class<?> c = null;
try{
c = Class.forName(“com.Licht._18.Student”);
System.out.println(c.getName());
}catch(ClassNotFountException e){
e.printStackTrace();
}
Student s = null;
Constructor<?>[] cons = c.getConstructors(); //返回一个包含某些 Constructor 对象的数
// 组,反映此 Class 对象所表示的类的所有公共构造方法。
try{
s = (Student) cons[0].newInstance(“Licht”, 24); //第0个构造器
}catch(InstantiationException e){
e.printStackTrace();
}

19. 集合

需要存储多个元素的结构时,可以用数组存储。但是数组也有它的弊端,使用的时候,必须先定义好长度,也就是数组的长度是固定,不能根据我们的需求自动变长或者变短。
Collection类是所有集合的父类。
1) Set集合:无序集合,不允许元素重复
是Collection接口的子接口,没有对Collection接口进行扩展,功能较弱。
HashSet
2) List集合:有序集合,允许元素重复
List接口的主要实现类有ArrayList,和LinkedList。
在数据量不大的情况下,这两个类性能差别不大,一般情况下,集合里的元素很少变化的,一般用ArrayList,假如集合里元素经常变动,要用LinkedList;底层实现有差别的。

3) Queue集合
队列集合,允许在两端添加、删除元素。
4) Map集合
key-value之间存在一一对应的映射关系。
HashMap
5) 操作集合的工具类Collections

20. 集合的遍历

Iterator、foreach、Lambda表达式
1) 使用Lamda表达式遍历集合
使用的是Iterator接口的forEach()方法,传入的形参是Lamda表达式。
2) 使用Iterator接口遍历集合
定义集合;
获取集合对应的Iterator迭代器;

1
2
3
while(it.hasNext()){
= it.next(); //返回的是Object类型,需要强制类型转换
}

3) 使用Lamda表达式遍历Iterator
4) 使用foreach循环遍历集合
5) 使用Predicate操作集合,它是函数式接口

21. 多线程

程序里同时执行多个任务并且加以控制,同时干多个事,能充分利用cpu 内存等硬件设备,提高程序运行效率。
1)创建、启动线程:
extends Thread:通过继承获取当前线程this(常省略)
implements Runnable|Callable:通过实现接口获取当前线程Thread.currenThread()
2) 线程的声明周期
(1) 新建new;
(2) 就绪start(),但并未立即执行,让当前线程(主线程)睡眠1ms:Thread.sleep(1);
(3) 运行:执行run()方法中的线程执行体;
(4) 阻塞
(5) 死亡
3) 控制线程
join():由当前使用线程的程序调用线程对象.join(),调用join()方法的线程将先执行。
setDaemonThread()设置成后台线程。
sleep():暂停,进入阻塞。
yield():暂停,进入就绪。

22. 线程同步

1) 同步代码块
同步方法:非static的同步监视器为this,即为调用该方法的对象
2) Lock:显示定义同步对象
Lock的实现类:ReentrantLock(可重入锁:一个线程可以对已被加锁的ReentrantLock锁再次加锁。)
ReadWriteLock(读写锁)的实现类:ReentrantReadWriteLock <-Java8新增的StampedLock可代替它。

23. 线程通信

程序通常无法准确地控制线程的轮换执行,但java提供了一些机制来保证线程协调运行。

1) synchronized:wait()、notify()、notifyAll()。
2) Lock对象:await()、signal()、signalAll()。
3) BlockingQueue(阻塞队列)
ThreadGroup:可对多个线程同时控制

24. 线程其它

1) 线程池:系统启动一个新线程的成本是比较高的,因为它涉及操作系统交互。当系统中需要创建大量生存周期短暂的线程时,更应该考虑用线程池。
2) 线程安全。
注意:
this关键字总是指向调用该方法的对象:

  • 在构造器中使用this引用时,this总是引用该构造器正在初始化的对象。对构造器正在初始化的对象的成员变量赋值。
  • 在方法中引用调用该方法的对象。它所代表的对象只能是当前类,只有该方法被调用时,this所代表的对象才被确定下来。谁在调用这个方法,this就代表谁。
    super关键字
    在子类中访问被覆盖的父类变量:super.a
    子类构造器调用父类构造器:super(a),其中a是变量
    类若没有提供构造器,使用它的静态方法来获取实例。
    3) 常用方法:
    (1) getName(); 返回该线程的名称。
    (2) currentThread();返回对当前正在执行的线程对象的引用。
    (3) isAlive();测试线程是否处于活动状态。
    (4) sleep();线程休眠。
    (5) setPriority(int newPriority);更改线程的优先级。
    (6) yield();暂停当前正在执行的线程对象,并执行其他线程。

    25. IO流

    1)节点流:

2)处理流:

26. 文件操作File类

1)public boolean mkdir() 创建此抽象路径名指定的目录。
2)public boolean createNewFile() 创建一个文件
3)public boolean delete() 删除此抽象路径名表示的文件或目录。如果此路径名表示一个目录,则该目录必须为空才能删除。
4)public boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
5)public File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
6)public boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。

27. 常用IO流

1) 处理流的用法
只需要在创建处理流时传入一个节点流作为构造参数即可。
2) 转换流
将字节流转换成字符流:InputStreamReader,OutputStreamWriter。
3) 带缓冲的输入和输出流:BufferedInputStream和BufferedOutputStream
这里缓冲的概念,就是在A,B之间建立内存缓冲区,读取得快,就先放缓冲区,然后再从缓冲区写入指定目标,和没有缓冲比,效率快很多。
4) Reader和Writer
主要用于文本的读取和写入,一般使用的实现类是FileReader和FileWriter。

28. debug断点调试

F6单步执行
F8执行完成,假如后面还有断点 执行到下一个断点处
F5进入方法
选中表达式,ctrl + shift + I查看表示式整体的值。
运行时动态修改变量的值。在企业级开发中,往往搞点测试数据麻烦,所以直接debug的时候随便改数据,来进行各种测试,比较方便。